﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading.Tasks;

namespace boe2.irc
{
    public class IrcConnection
    {
        public delegate void DataReceivedEventHandler(DataReceivedEventArgs args);
        public delegate void CommandSentEventHandler(CommandSentEventArgs args);
        public event EventHandler Connected;
        public event CommandSentEventHandler CommandSent;
        public event DataReceivedEventHandler DataReceived;
        public String HostName { get; private set; }
        public Int32 Port { get; private set; }
		public String NickName;
        private Socket socket;
        private Task reader;
        public IrcConnection(String hostname, Int32 port)
        {
            HostName = hostname;
            Port = port;
        }

        public void Start()
        {
            TcpClient client = new TcpClient();
            client.BeginConnect(HostName, Port, new AsyncCallback(ConnectedEvent), client);
        }

        public void SendCommand(IrcCommand command)
        {
            byte[] message = Encoding.ASCII.GetBytes(command.CommandText);
			while (socket.Available > 0)
			{
				System.Threading.Thread.Sleep(1);
			}
			socket.BeginSend(message, 0, message.Length, SocketFlags.None, new AsyncCallback(CommandSentEvent), new SentCommandArgs(command, socket));
        }

        private void ConnectedEvent(IAsyncResult ar)
        {
			TcpClient client = (TcpClient)ar.AsyncState;
			client.EndConnect(ar);
			socket = client.Client;
            reader = new Task(new Action(() => ReadPort()));
            if (reader.Status != TaskStatus.Running)
            {
                reader.Start();
            }
            if (Connected != null)
            {
                Connected(this, new EventArgs());
            }
        }

        private void CommandSentEvent(IAsyncResult ar)
        {
			SentCommandArgs args = (SentCommandArgs)ar.AsyncState;
			Int32 sentBytes = args.Socket.EndSend(ar);
            if (CommandSent != null)
            {
                CommandSent(new CommandSentEventArgs(args.Command, sentBytes));
            }
        }

        private void ReadPort()
        {
            while (true)
            {
                if (socket.Available > 0 && DataReceived != null)
                {
                    String data = "";
                    byte[] buffer = new byte[socket.Available];
					socket.Receive(buffer);
					data = Encoding.ASCII.GetString(buffer);
                    IrcMessage[] messages = IrcMessage.Parse(data);
                    DataReceived(new DataReceivedEventArgs(messages));
                }
            }
        }
    }

    public class DataReceivedEventArgs : EventArgs
    {
        public IrcMessage[] Data { get; set; }
        public DataReceivedEventArgs(IrcMessage[] data)
        {
            Data = data;
        }
    }

    public class CommandSentEventArgs : EventArgs
    {
        public IrcCommand Data { get; set; }
		public Int32 BytesSent { get; set; }
        public CommandSentEventArgs(IrcCommand data, Int32 bytesSent)
        {
            Data = data;
			BytesSent = bytesSent;
        }
    }

	public class SentCommandArgs
	{
		public IrcCommand Command { get; private set; }
		public Socket Socket { get; private set; }

		public SentCommandArgs(IrcCommand command, Socket socket)
		{
			Command = command;
			Socket = socket;
		}
	}
}
